home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Files / MoreIsBetter / MIB-Libraries / Sources / MoreOpenAndSave.cp < prev    next >
Encoding:
Text File  |  1998-09-25  |  25.8 KB  |  1,031 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        MoreOpenAndSave.cp
  3.  
  4.     Contains:    wraps Navigation Services in an API which looks like Standard File
  5.  
  6.     Written by:    Pete Gontier
  7.  
  8.     Copyright:    Copyright (c) 1997-1998 Apple Computer, Inc.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.  
  20.          <7>      9/1/98    PCG     Universal Headers 3.2
  21.          <6>     8/26/98    PCG     in MOASI_HandleEvent, adjust timing of increment of foundItem
  22.          <5>     27/7/98    QQQ     Fix unused variable warnings.
  23.          <4>     7/24/98    PCG     obviate MOASI_GetSomeCurrentProcessInformation and
  24.                                     MOASI_PLstrcpy
  25.          <3>     7/11/98    PCG     add back old version history
  26.          <2>     7/11/98    PCG     initial checkin
  27. */
  28.  
  29.     //    who        when        what
  30.     //    ---        --------    ------------------------------------------------------
  31.     //    PCG        12/10/97    new today; MOASI_StandardGetFile
  32.     //    PCG        12/13/97    MOASI_StandardPutFile
  33.     //    PCG        01/19/98    MOASI_StandardOpenDialog
  34.     //                            (for Translation Manager)
  35.     //    PCG        04/02/98    fork from original MOAS project
  36.     //                            (lose the system extension)
  37.     //    PCG        04/02/98    MOASI_CustomGetFile
  38.     //    PCG        04/02/98    MOASI_CustomPutFile
  39.     //    PCG        04/07/98    eliminate calls to PACK3;
  40.     //                            eliminate MOASI_SwapAvoidFallBack
  41.     //    PCG        04/08/98    split READ ME into separate file
  42.     //    PCG        04/22/98    merged Yan's changes to demo app
  43.     //    PCG        04/22/98    MOAS_ => MOASI_
  44.     //    PCG        04/22/98    threw out TempRoutineDescriptor
  45.     //    PCG        04/22/98    restructured MOASI_GetFile parameters to
  46.     //                             better support MOASI_CustomGetFile
  47.     //    PCG        04/22/98    MOASI_CustomGetFile displays custom items
  48.     //    PCG        04/22/98    MOASI_CustomGetFileCore (to support
  49.     //                            filtering of directories)
  50.     //    PCG        05/04/98    MOASI_StandardGetFilePreview (for QuickTime)
  51.     //    PCG        05/04/98    MOASI_CustomGetFilePreview (for QuickTime)
  52.  
  53. #pragma mark INCLUDES
  54.  
  55. #define OLDROUTINELOCATIONS            0
  56. #define OLDROUTINENAMES                0
  57. #define SystemSevenFiveOrLater        1
  58.  
  59. #include "Navigation.h"
  60. #include "MoreOpenAndSave.h"
  61. #include "MoreProcesses.h"
  62.  
  63. #include <LowMem.h>
  64. #include <Script.h>
  65. #include <Processes.h>
  66. #include <Resources.h>
  67. #include <MacMemory.h>
  68. #include <Sound.h>
  69. #include <PLStringFuncs.h>
  70.  
  71. #pragma mark CONSTANTS
  72.  
  73. enum
  74. {
  75.     kUseOpenResourceTypes    = -2,
  76.     kAllFileTypes            = -1,
  77.     kOpenResType            = 'open',
  78.     kStandardOpenResID        = 128
  79. };
  80.  
  81. #pragma mark MACROS
  82.  
  83. #define Assert(x) \
  84.     do { if (!(x)) \
  85.         ::DebugStr ("\p-=-=-=-=-=-\rassertion failed in file \"" __FILE__ \
  86.             "\":\r  " #x "\r-=-=-=-=-=-"); } while (0)
  87.  
  88. #pragma mark TYPES
  89.  
  90. typedef struct
  91. {
  92.     bool                    filterDirs;        // true only for CustomGetFile
  93.     FileFilterYDUPP            ffYDUPP;        // non-NIL for CustomGetFile
  94.     void                    *userData;        // whatever the caller passed in
  95.     short                    ditlResID;        // non-zero if need items added
  96.     ModalFilterYDUPP        modalFilter;    // may be non-nil for CustomGetFile and CustomPutFile
  97.     DlgHookYDProcPtr        dialogHook;        // may be non-nil for CustomGetFile and CustomPutFile
  98. }
  99. tBridgeData, *tBridgeDataP, **tBridgeDataH;
  100.  
  101. class NavLoader
  102. {
  103.         bool fNeedUnload;
  104.  
  105.     public :
  106.  
  107.         NavLoader (OSErr &err)
  108.         {
  109.             err = ::NavLoad ( );
  110.             fNeedUnload = err ? false : true;
  111.         }
  112.  
  113.         ~NavLoader (void)
  114.         {
  115.             if (fNeedUnload)
  116.             {
  117.                 OSErr err = ::NavUnload ( );
  118.                 Assert (err == noErr);
  119.             }
  120.         }
  121. };
  122.  
  123. template <class T> class TempBuffer
  124. {
  125.     //
  126.     //    This is a utility class for holding an arbitrary pointer temporarily.
  127.     //    This class is useful simply because class destructors are guaranteed
  128.     //    to run when instances of the class fall out of scope. This means we
  129.     //    don't have to have nested code to help us decide whether to call
  130.     //    DisposePtr.
  131.     //
  132.  
  133.         T *fPtr;
  134.  
  135.     public :
  136.  
  137.         explicit TempBuffer (Ptr t, OSErr &err)
  138.         {
  139.             if (!t)
  140.             {
  141.                 err = ::MemError ( );
  142.                 Assert (err != noErr);
  143.                 fPtr = nil;
  144.             }
  145.  
  146.             Assert (::PtrZone (t) != nil);
  147.             Assert (::MemError ( ) == noErr);
  148.  
  149.             fPtr = (T*) t;
  150.         }
  151.  
  152.         ~TempBuffer (void)
  153.         {
  154.             if (fPtr)
  155.             {
  156.                 ::DisposePtr (Ptr (fPtr));
  157.                 Assert (::MemError ( ) == noErr);
  158.             }
  159.         }
  160.  
  161.         T * operator -> (void) const
  162.         {
  163.             return fPtr;
  164.         }
  165.  
  166.         operator T * (void) const
  167.         {
  168.             return fPtr;
  169.         }
  170.  
  171.         T & operator * (void) const
  172.         {
  173.             return *fPtr;
  174.         }
  175. };
  176.  
  177. #pragma mark -
  178.  
  179. static pascal OSErr MOASI_FSpGetCatInfo (const FSSpec &fss, CInfoPBPtr &cipbp)
  180. {
  181.     //
  182.     //    Create a parameter block for PBGetCatInfo.
  183.     //    Fill it with data based on 'fss', appending
  184.     //    bytes for the filename at the end of the block.
  185.     //    Ask PBGetCatInfo to complete the block.
  186.     //    Produce a pointer to the augmented block.
  187.     //    Caller is expected to dispose the block.
  188.     //
  189.  
  190.     OSErr err = noErr;
  191.  
  192.     cipbp = CInfoPBPtr (NewPtrClear (sizeof (*cipbp) + sizeof (Str31)));
  193.  
  194.     if (!cipbp)
  195.         err = MemError ( );
  196.     else
  197.     {
  198.         StringPtr entityName = StringPtr (cipbp) + sizeof (*cipbp);
  199.  
  200.         PLstrcpy (entityName,fss.name);
  201.  
  202.         cipbp->dirInfo.ioNamePtr = entityName;
  203.         cipbp->dirInfo.ioVRefNum = fss.vRefNum;
  204.         cipbp->dirInfo.ioDrDirID = fss.parID;
  205.  
  206.         err = PBGetCatInfoSync (cipbp);
  207.  
  208.         if (err)
  209.         {
  210.             DisposePtr (Ptr (cipbp));
  211.             Assert (MemError ( ) == noErr);
  212.         }
  213.     }
  214.  
  215.     return err;
  216. }
  217.  
  218. static pascal OSErr MOASI_AEGetCatInfo (const AEDesc &fssDesc, CInfoPBPtr &cipbp)
  219. {
  220.     //
  221.     //    Given an AEDesc of typeFSS, produce a filled-out
  222.     //    parameter block from PBGetCatInfo. Caller is
  223.     //    expected to dispose the block.
  224.     //
  225.  
  226.     Assert (fssDesc.descriptorType == typeFSS);
  227.  
  228.     cipbp = nil;
  229.  
  230.     SInt8 hState = HGetState (fssDesc.dataHandle);
  231.     Assert (MemError ( ) == noErr);
  232.     MoveHHi (fssDesc.dataHandle);
  233.     Assert (MemError ( ) == noErr);
  234.     HLock (fssDesc.dataHandle);
  235.     Assert (MemError ( ) == noErr);
  236.  
  237.     FSSpecPtr fssP = FSSpecPtr (*(fssDesc.dataHandle));
  238.  
  239.     OSErr err = MOASI_FSpGetCatInfo (*fssP,cipbp);
  240.  
  241.     HSetState (fssDesc.dataHandle, hState);
  242.     Assert (MemError ( ) == noErr);
  243.  
  244.     return err;
  245. }
  246.  
  247. static pascal OSErr MOASI_AECoerceAndGetCatInfo (const AEDesc &aeDesc, CInfoPBPtr &cipbp)
  248. {
  249.     //
  250.     //    Given an AEDesc which can be coerced to typeFSS,
  251.     //    produce a filled-out parameter block from PBGetCatInfo.
  252.     //    Caller is expected to dispose the block.
  253.     //
  254.  
  255.     cipbp = nil;
  256.  
  257.     OSErr err = noErr;
  258.  
  259.     AEDesc fssDesc;
  260.  
  261.     if (!(err = AECoerceDesc (&aeDesc, typeFSS, &fssDesc)))
  262.     {
  263.         err = MOASI_AEGetCatInfo (fssDesc,cipbp);
  264.         OSErr err2 = AEDisposeDesc (&fssDesc);
  265.         Assert (err2 == noErr);
  266.     }
  267.  
  268.     return err;
  269. }
  270.  
  271. static pascal OSErr MOASI_AddDialogItems (short ditlResID, NavContext context)
  272. {
  273.     OSErr err = noErr;
  274.  
  275.     Handle ditl = GetResource ('DITL',ditlResID);
  276.  
  277.     if (!ditl)
  278.     {
  279.         err = ResError ( );
  280.         if (!err) err = resNotFound;
  281.     }
  282.     else
  283.     {
  284.         err = NavCustomControl (context, kNavCtlAddControlList, ditl);
  285.         ReleaseResource (ditl);
  286.         Assert (ResError ( ) == noErr);
  287.     }
  288.  
  289.     return err;
  290. }
  291.  
  292. static pascal OSErr MOASI_NegotiateCustomRectSize (Rect &customRect)
  293. {
  294.     OSErr err = noErr;
  295.  
  296.     //
  297.     //    We assume the documented minimum size will be accepted,
  298.     //    but we are ready to accept something larger if Nav offers.
  299.     //
  300.  
  301.     enum
  302.     {
  303.         kMinimumDocumentedWidth        = 400,
  304.         kMinimumDocumentedHeight    = 40
  305.     };
  306.  
  307.     if (customRect.bottom)
  308.         Assert (kMinimumDocumentedHeight <= customRect.bottom - customRect.top);
  309.     else
  310.         customRect.bottom = customRect.top + kMinimumDocumentedHeight;
  311.  
  312.     if (customRect.right)
  313.         Assert (kMinimumDocumentedWidth <= customRect.right - customRect.left);
  314.     else
  315.         customRect.right = customRect.left + kMinimumDocumentedWidth;
  316.  
  317.     return err;
  318. }
  319.  
  320. static pascal OSErr MOASI_HandleEvent
  321.     (const tBridgeData &bridgeData, DialogPtr navDialog, const EventRecord &event, NavContext context)
  322. {
  323.     OSErr err = noErr;
  324.  
  325.     if (event.what == mouseDown)
  326.     {
  327.         WindowPtr whichWindow;
  328.         short fwPartCode = FindWindow (event.where,&whichWindow);
  329.  
  330.         if (fwPartCode == inContent && navDialog == whichWindow)
  331.         {
  332.             Point        localWhere = event.where;
  333.             GrafPtr        preservedPort;
  334.  
  335.             GetPort (&preservedPort);
  336.             SetPort (navDialog);
  337.             // here's what we would do in a perfect world
  338.             // GlobalToLocal (&localWhere);
  339.             // but instead, we must hack to coddle Nav 1.0
  340.             GetMouse (&localWhere);
  341.             SetPort (preservedPort);
  342.  
  343.             DialogItemIndexZeroBased foundItem = FindDialogItem (navDialog,localWhere);
  344.  
  345.             if (foundItem >= 0)
  346.             {
  347.                 DialogItemIndexZeroBased firstCustomItemIndex;
  348.                 if (!(err = NavCustomControl (context,kNavCtlGetFirstControlID,&firstCustomItemIndex)))
  349.                 {
  350.                     if (foundItem >= firstCustomItemIndex)
  351.                     {
  352.                         EventRecord            abuseEvent = event;
  353.                         DialogItemIndex        abuseIndex = foundItem + 1;
  354.  
  355.                         (void) CallModalFilterYDProc (bridgeData.modalFilter, navDialog, &abuseEvent, &abuseIndex, bridgeData.userData);
  356.                     }
  357.                 }
  358.             }
  359.         }
  360.     }
  361.  
  362.     return err;
  363. }
  364.  
  365. static pascal void MOASI_EventFilterBridge
  366.     (NavEventCallbackMessage message, NavCBRecPtr param, NavCallBackUserData myData)
  367. {
  368.     OSErr err = noErr;
  369.  
  370.     tBridgeDataP bridgeData = tBridgeDataP (myData);
  371.  
  372.     switch (message)
  373.     {
  374.         case kNavCBEvent :
  375.  
  376.             if (bridgeData->modalFilter)
  377.                 err = MOASI_HandleEvent (*bridgeData,param->window,*(param->eventData.eventDataParms.event),param->context);
  378.             break;
  379.  
  380.         case kNavCBCustomize :
  381.  
  382.             if (bridgeData->ditlResID)
  383.                 err = MOASI_NegotiateCustomRectSize (param->customRect);
  384.             break;
  385.  
  386.         case kNavCBStart :
  387.  
  388.             if (bridgeData->ditlResID)
  389.                 err = MOASI_AddDialogItems (bridgeData->ditlResID, param->context);
  390.             break;
  391.     }
  392.  
  393.     if (err) SysBeep (-1);
  394. }
  395.  
  396. static pascal Boolean MOASI_FileSystemFilterBridgeFromYD (CInfoPBPtr pb, void *yourDataPtr)
  397. {
  398.     return CallFileFilterProc (FileFilterUPP (yourDataPtr), pb);
  399. }
  400.  
  401. static pascal Boolean MOASI_FileSystemFilterBridgeToSFYD
  402.     (AEDesc *item, void *, NavCallBackUserData callBackUD, NavFilterModes)
  403. {
  404.     //
  405.     //    Given Nav file filter function parameters, translate to Standard
  406.     //    File terms and call a PACK 3 -style file filter function.
  407.     //    This function is called only from within Nav.
  408.     //
  409.  
  410.     Assert (callBackUD);
  411.     tBridgeDataP userDataP = tBridgeDataP (callBackUD);
  412.     Assert (nil != userDataP->ffYDUPP);
  413.  
  414.     OSErr        err        = noErr;
  415.     Boolean        keepIt    = true;
  416.     CInfoPBPtr    cipbp    = nil;
  417.  
  418.     if (!(err = MOASI_AECoerceAndGetCatInfo (*item,cipbp)))
  419.     {
  420.         Assert (cipbp != nil);
  421.  
  422.         if (cipbp->hFileInfo.ioFlAttrib & ioDirMask)
  423.         {
  424.             if (userDataP->filterDirs)
  425.                 keepIt = !CallFileFilterYDProc (userDataP->ffYDUPP, cipbp, userDataP->userData);
  426.         }
  427.         else
  428.         {
  429.             const OSType kFileTypeDoesNotExist = 'ƒldr';
  430.  
  431.             if (cipbp->hFileInfo.ioFlFndrInfo.fdType == kFileTypeDoesNotExist)
  432.                 keepIt = false;
  433.             else
  434.                 keepIt = !CallFileFilterYDProc (userDataP->ffYDUPP, cipbp, userDataP->userData);
  435.         }
  436.  
  437.         DisposePtr (Ptr (cipbp));
  438.         Assert (MemError ( ) == noErr);
  439.     }
  440.  
  441.     return err ? false : keepIt;
  442. }
  443.  
  444. static pascal OSErr MOASI_NewNavTypeList
  445.     (short numTypes, ConstSFTypeListPtr typeList, NavTypeListHandle &result)
  446. {
  447.     //
  448.     //    Given PACK 3 filtering data, create and fill a
  449.     //    NavTypeListHandle. Caller is expected to dispose it.
  450.     //
  451.  
  452.     OSErr err = noErr;
  453.  
  454.     result = nil;
  455.  
  456.     //
  457.     //    If caller wants the old StandardOpenDialog behavior,
  458.     //    go get the standard open resource and copy it so it
  459.     //    can be disposed without any inconvenience.
  460.     //
  461.  
  462.     if (numTypes == kUseOpenResourceTypes)
  463.     {
  464.         UInt8 preservedResLoad = LMGetResLoad ( );
  465.  
  466.         SetResLoad (false);
  467.  
  468.         Handle openResource = GetResource (kOpenResType,kStandardOpenResID);
  469.         err = ResError ( );
  470.  
  471.         SetResLoad (preservedResLoad ? true : false);
  472.  
  473.         if (!openResource)
  474.         {
  475.             if (!err) err = resNotFound;
  476.         }
  477.         else
  478.         {
  479.             err = noErr;
  480.  
  481.             Boolean resourceWasLoaded = *result ? true : false;
  482.  
  483.             if (!resourceWasLoaded)
  484.             {
  485.                 LoadResource (openResource);
  486.                 err = ResError ( );
  487.             }
  488.  
  489.             if (!err)
  490.             {
  491.                 SInt8 hState = HGetState (openResource);
  492.                 Assert (!MemError ( ));
  493.                 HNoPurge (openResource);
  494.                 Assert (!MemError ( ));
  495.  
  496.                 Handle openResourceCopy = openResource;
  497.  
  498.                 if (!(err = HandToHand (&openResourceCopy)))
  499.                     result = NavTypeListHandle (openResourceCopy);
  500.  
  501.                 HSetState (openResource,hState);
  502.                 Assert (!MemError ( ));
  503.  
  504.                 if (!resourceWasLoaded)
  505.                 {
  506.                     EmptyHandle (Handle (result));
  507.                     Assert (!MemError ( ));
  508.                 }
  509.             }
  510.  
  511.             ReleaseResource (openResource);
  512.             Assert (!ResError ( ));
  513.         }
  514.     }
  515.  
  516.     //
  517.     //    If the caller specified some file types, build the corresponding
  518.     //    Nav data structure, which just happens to be in the same format as
  519.     //    the old 'kind' resource. (What a coincidence!)
  520.     //
  521.  
  522.     else if (numTypes > 0)
  523.     {
  524.         result = NavTypeListHandle (NewHandle (sizeof (NavTypeList) + (numTypes * sizeof (OSType))));
  525.  
  526.         if (!result)
  527.             err = MemError ( );
  528.         else
  529.         {
  530.             TempBuffer <ProcessInfoRec> pirP (NewPtr (sizeof (ProcessInfoRec)), err);
  531.  
  532.             if (!err && !(err = GetSomeProcessInfo (nil,pirP)))
  533.             {
  534.                 (**result).componentSignature    = pirP->processSignature;
  535.                 (**result).osTypeCount            = numTypes;
  536.  
  537.                 BlockMoveData (typeList, (**result).osType, numTypes * sizeof (OSType));
  538.             }
  539.  
  540.             if (err)
  541.             {
  542.                 DisposeHandle (Handle (result));
  543.                 result = nil;
  544.             }
  545.         }
  546.     }
  547.     
  548.     return err;
  549. }
  550.  
  551. static pascal void MOASI_ClearStandardFileReply (StandardFileReply &sfReply)
  552. {
  553.     //
  554.     //    One would hope it's obvious what this function does
  555.     //    from reading it. WHY and WHEN you'd want to do this
  556.     //    is another matter. Read the comments in the callers.
  557.     //
  558.  
  559.     sfReply.sfGood                = false;
  560.     sfReply.sfReplacing            = false;
  561.     sfReply.sfType                = 0;
  562.     sfReply.sfFile.parID        = 0;
  563.     sfReply.sfFile.vRefNum        = 0;
  564.     sfReply.sfFile.name [0]        = 0;
  565.     sfReply.sfScript            = 0;
  566.     sfReply.sfFlags                = 0;
  567.     sfReply.sfIsFolder            = false;
  568.     sfReply.sfIsVolume            = false;
  569.     sfReply.sfReserved1            = 0;
  570.     sfReply.sfReserved2            = 0;
  571. }
  572.  
  573. static pascal OSErr MOASI_TranslateNavReply
  574.     (const NavReplyRecord &navReply, StandardFileReply &sfReply, bool simulatingGet)
  575. {
  576.     //
  577.     //    Given a Nav reply record, translate it to a PACK 3 reply record.
  578.     //    We assume the PACK 3 reply record has been completely cleared
  579.     //    (probably by MOASI_ClearStandardFileReply).
  580.     //
  581.  
  582.     OSErr err = noErr;
  583.  
  584.     AEDesc        firstDesc;
  585.     AEKeyword    ignoreKeyword;
  586.  
  587.     if (!(err = AEGetNthDesc (&(navReply.selection), 1, typeFSS, &ignoreKeyword, &firstDesc)))
  588.     {
  589.         MoveHHi (firstDesc.dataHandle);
  590.         Assert (MemError ( ) == noErr);
  591.         HLock (firstDesc.dataHandle);
  592.         Assert (MemError ( ) == noErr);
  593.  
  594.         FSSpecPtr fssP = FSSpecPtr (*(firstDesc.dataHandle));
  595.  
  596.         //
  597.         //    This is the same logic PACK 3 uses;
  598.         //    we emulate it here even though we don't expect
  599.         //    to ever be called with navReply.validRecord false.
  600.         //    In fact, we assert that we are not. Yes, I do
  601.         //    know how many A's are in "anal".
  602.         //
  603.  
  604.         sfReply.sfFile.vRefNum    = fssP->vRefNum;
  605.         sfReply.sfFile.parID    = fssP->parID;
  606.  
  607.         sfReply.sfGood = navReply.validRecord;
  608.  
  609.         Assert (sfReply.sfGood == true);
  610.  
  611.         if (sfReply.sfGood)
  612.         {
  613.             PLstrcpy (sfReply.sfFile.name, fssP->name);
  614.  
  615.             if (simulatingGet)
  616.             {
  617.                 //
  618.                 //    PACK 3 does not have to get the catalog info;
  619.                 //    it caches what it needs. We don't have that luxury.
  620.                 //
  621.  
  622.                 CInfoPBPtr cipbp;
  623.  
  624.                 if (!(err = MOASI_FSpGetCatInfo (*fssP,cipbp)))
  625.                 {
  626.                     sfReply.sfScript        = cipbp->hFileInfo.ioFlXFndrInfo.fdScript;
  627.                     sfReply.sfFlags            = cipbp->hFileInfo.ioFlFndrInfo.fdFlags;
  628.  
  629.                     if (!(cipbp->hFileInfo.ioFlAttrib & ioDirMask))
  630.                         sfReply.sfType = cipbp->hFileInfo.ioFlFndrInfo.fdType;
  631.                     else if (fssP->parID == fsRtParID)
  632.                         sfReply.sfIsVolume = true;
  633.                     else
  634.                         sfReply.sfIsFolder = true;
  635.  
  636.                     DisposePtr (Ptr (cipbp));
  637.                     Assert (noErr == MemError ( ));
  638.                 }
  639.             }
  640.             else
  641.             {
  642.                 //
  643.                 //    Yan's code has a comment that wonders whether we
  644.                 //    should be getting the script from Nav. It's unlikely
  645.                 //    to have changed between then and now, but it would be
  646.                 //    cleaner.
  647.                 //
  648.                 //    Since I'm no Script Manager expert, I should look up
  649.                 //    whether it's safe to call GetScriptManagerVariable on
  650.                 //    all systems under which Nav is supported.
  651.                 //
  652.  
  653.                 sfReply.sfScript = GetScriptManagerVariable (smKeyScript);
  654.  
  655.                 //
  656.                 //    PACK 3 sets sfReplacing on the fly, just after
  657.                 //    the user confirms whether it's OK. We don't confirm
  658.                 //    anything ourselves, so we must set it here.
  659.                 //
  660.  
  661.                 sfReply.sfReplacing = navReply.replacing;
  662.             }
  663.         }
  664.  
  665.         OSErr err2 = AEDisposeDesc (&firstDesc);
  666.         Assert (err2 == noErr);
  667.     }
  668.  
  669.     return err;
  670. }
  671.  
  672. static pascal OSErr MOASI_GetFile
  673.     (short numTypes, ConstSFTypeListPtr typeList, StandardFileReply *reply, const tBridgeData *bridgeData,
  674.         NavEventUPP eventUPP, NavObjectFilterUPP objectFilterUPP)
  675. {
  676.     //
  677.     //    Given PACK 3 parameters, translate them to Nav terms and
  678.     //    call Nav to simulate StandardGetFile.
  679.     //
  680.  
  681.     OSErr err = noErr;
  682.  
  683.     //
  684.     //    We explicitly load and unload Nav because callers of Standard
  685.     //    File may expect it to be completely removed from memory when
  686.     //    it is done, and Nav has a deferred unloading scheme.
  687.     //
  688.  
  689.     NavLoader navLoader (err);
  690.     if (err) return err;
  691.  
  692.     //
  693.     //    Allocate some buffers we'll need; they'll be automagically disposed.
  694.     //
  695.  
  696.     TempBuffer <NavReplyRecord> navReply (NewPtr (sizeof (NavReplyRecord)), err);
  697.     if (err) return err;
  698.     TempBuffer <NavDialogOptions> navOptions (NewPtr (sizeof (NavDialogOptions)), err);
  699.     if (err) return err;
  700.  
  701.     //
  702.     //    Ask Nav to fill in its default dialog options.
  703.     //    We will over-ride some of them later.
  704.     //
  705.  
  706.     err = NavGetDefaultDialogOptions (navOptions);
  707.     if (err) return err;
  708.  
  709.     NavTypeListHandle navTypeList = nil;
  710.  
  711.     err = MOASI_NewNavTypeList (objectFilterUPP ? (numTypes == kUseOpenResourceTypes ? -1 : numTypes) : numTypes, typeList, navTypeList);
  712.  
  713.     if (!err || err == resNotFound) // it's OK if we could not find the 'open' resource
  714.     {
  715.         navOptions->preferenceKey        = (**navTypeList).componentSignature;
  716.         navOptions->dialogOptionFlags    = kNavAllowPreviews;
  717.  
  718.         if (numTypes == -1 || numTypes == 0)
  719.         {
  720.             navOptions->dialogOptionFlags |= kNavNoTypePopup;
  721.             navOptions->dialogOptionFlags |= kNavDontAutoTranslate;
  722.         }
  723.  
  724.         if (objectFilterUPP)
  725.             navOptions->dialogOptionFlags |= kNavAllowInvisibleFiles;
  726.  
  727.         NavCallBackUserData navCallBackUserData = NavCallBackUserData (bridgeData);
  728.  
  729.         err = NavGetFile (nil, navReply, navOptions, eventUPP, nil, objectFilterUPP, navTypeList, navCallBackUserData);
  730.  
  731.         if (err == userCanceledErr)
  732.             err = noErr;
  733.         else if (!err)
  734.         {
  735.             if (navReply->validRecord)
  736.             {
  737.                 err = MOASI_TranslateNavReply (*navReply,*reply,true);
  738.             }
  739.  
  740.             OSErr err2 = NavDisposeReply (navReply);
  741.             Assert (err2 == noErr);
  742.         }
  743.  
  744.         DisposeHandle (Handle (navTypeList));
  745.         Assert (noErr == MemError ( ));
  746.     }
  747.  
  748.     return err;
  749. }
  750.  
  751. static pascal OSErr MOASI_PutFile
  752.     (ConstStr255Param prompt, ConstStr255Param defaultName, StandardFileReply *reply)
  753. {
  754.     //
  755.     //    Given PACK 3 parameters, translate them to Nav terms and
  756.     //    call Nav to simulate StandardGetFile.
  757.     //
  758.  
  759.     OSErr err = noErr;
  760.  
  761.     //
  762.     //    We explicitly load and unload Nav because callers of Standard
  763.     //    File may expect it to be completely removed from memory when
  764.     //    it is done, and Nav has a deferred unloading scheme.
  765.     //
  766.  
  767.     NavLoader navLoader (err);
  768.     if (err) return err;
  769.  
  770.     //
  771.     //    Allocate some buffers we'll need; they'll be automagically disposed.
  772.     //
  773.  
  774.     TempBuffer <NavReplyRecord> navReply (NewPtr (sizeof (NavReplyRecord)), err);
  775.     if (err) return err;
  776.     TempBuffer <NavDialogOptions> navOptions (NewPtr (sizeof (NavDialogOptions)), err);
  777.     if (err) return err;
  778.     TempBuffer <ProcessInfoRec> pirP (NewPtr (sizeof (ProcessInfoRec)), err);
  779.     if (err) return err;
  780.  
  781.     //
  782.     //    Get some information about the current process, including its creator code.
  783.     //
  784.  
  785.     err = GetSomeProcessInfo (nil,pirP);
  786.     if (err) return err;
  787.  
  788.     //
  789.     //    Ask Nav to fill in its default dialog options.
  790.     //    Over-ride the ones we don't like.
  791.     //
  792.  
  793.     err = NavGetDefaultDialogOptions (navOptions);
  794.     if (err) return err;
  795.  
  796.     navOptions->preferenceKey        = pirP->processSignature;
  797.     navOptions->dialogOptionFlags    = kNavNoTypePopup | kNavDontAddTranslateItems;
  798.  
  799.     PLstrcpy (navOptions->savedFileName,defaultName);
  800.     PLstrcpy (navOptions->message,prompt);
  801.  
  802.     err = NavPutFile (nil,navReply,navOptions,nil,0,0,nil);
  803.  
  804.     if (err == userCanceledErr)
  805.         err = noErr;
  806.     else if (!err)
  807.     {
  808.         if (navReply->validRecord)
  809.         {
  810.             err = MOASI_TranslateNavReply (*navReply,*reply,false);
  811.         }
  812.  
  813.         OSErr err2 = NavDisposeReply (navReply);
  814.         Assert (err2 == noErr);
  815.     }
  816.  
  817.     return err;
  818. }
  819.  
  820. static pascal OSErr MOASI_CustomGetFileCore (    volatile    FileFilterYDUPP                        fileFilter,
  821.                                                 volatile    short                                numTypes,
  822.                                                 volatile    ConstSFTypeListPtr                    typeList,
  823.                                                             StandardFileReply *        volatile    reply,
  824.                                                 volatile    short                                ditlResID,
  825.                                                 volatile    DlgHookYDProcPtr                    dialogHook,
  826.                                                 volatile    ModalFilterYDUPP                    modalFilter,
  827.                                                             void *                    volatile    yourDataPtr,
  828.                                                 volatile    Boolean                                filterDirs        )
  829. {
  830.     OSErr err = noErr;
  831.  
  832.     //
  833.     //    PACK 3 ALWAYS clears the record.
  834.     //    PACK 3 also fills in some fields even
  835.     //    when sfGood is false. I'm not sure how to
  836.     //    translate this, so I'm just going to punt it.
  837.     //    Nav's model is just too different.
  838.     //
  839.  
  840.     MOASI_ClearStandardFileReply (*reply);
  841.  
  842.     //
  843.     //    In order to add items to the dialog, we must specify a non-nil event filter
  844.     //    to Nav. If we specify a non-nil event filter, Nav will assume it's OK to
  845.     //    be movable-modal. If Nav is movable-modal, we need someone to handle events.
  846.     //    Ergo, it's not possible to add items to a dialog without handling events.
  847.     //
  848.  
  849.     Assert (!ditlResID || modalFilter);
  850.  
  851.     //
  852.     //    Before we bother to do anything more expensive than parameter validation,
  853.     //    we make sure Nav is around, so we don't waste time allocating memory which
  854.     //    will never be used.
  855.     //
  856.  
  857.     if (NavServicesAvailable ( ))
  858.     {
  859.         NavEventUPP eventUPP = nil;
  860.  
  861.         if (modalFilter || ditlResID || dialogHook)
  862.         {
  863.             eventUPP = NewNavEventProc (MOASI_EventFilterBridge);
  864.             if (!eventUPP) err = MemError ( );
  865.         }
  866.  
  867.         if (!err)
  868.         {
  869.             NavObjectFilterUPP objectFilterUPP = nil;
  870.  
  871.             if (fileFilter)
  872.             {
  873.                 objectFilterUPP = NewNavObjectFilterProc (MOASI_FileSystemFilterBridgeToSFYD);
  874.                 if (!objectFilterUPP) err = MemError ( );
  875.             }
  876.  
  877.             if (!err)
  878.             {
  879.                 tBridgeData bridgeData =
  880.                 {
  881.                     filterDirs,
  882.                     fileFilter,
  883.                     yourDataPtr,
  884.                     ditlResID,
  885.                     modalFilter,
  886.                     dialogHook
  887.                 };
  888.  
  889.                 err = MOASI_GetFile (numTypes,typeList,reply,&bridgeData,eventUPP,objectFilterUPP);
  890.                 if (objectFilterUPP) DisposeRoutineDescriptor (objectFilterUPP);
  891.             }
  892.  
  893.             if (eventUPP) DisposeRoutineDescriptor (eventUPP);
  894.         }
  895.     }
  896.  
  897.     return err;
  898. }
  899.  
  900. #pragma mark -
  901.  
  902. pascal OSErr MOASI_CustomGetFile (    volatile    FileFilterYDUPP                        fileFilter,
  903.                                     volatile    short                                numTypes,
  904.                                     volatile    ConstSFTypeListPtr                    typeList,
  905.                                                 StandardFileReply *        volatile    reply,
  906.                                     volatile    short                                ditlResID,
  907.                                     volatile    DlgHookYDProcPtr                    dialogHook,
  908.                                     volatile    ModalFilterYDUPP                    modalFilter,
  909.                                                 void *                    volatile    yourDataPtr        )
  910. {
  911.     //
  912.     //    Just pass params thru to CustomGetFileCore
  913.     //    and specify that we do want to filter directories.
  914.     //
  915.  
  916.     return MOASI_CustomGetFileCore (fileFilter,numTypes,typeList,reply,ditlResID,dialogHook,modalFilter,yourDataPtr,true);
  917. }
  918.  
  919. pascal OSErr MOASI_CustomPutFile (    volatile    ConstStr255Param                    prompt,
  920.                                     volatile    ConstStr255Param                    defaultName,
  921.                                                 StandardFileReply *        volatile    reply,
  922.                                     volatile    short                                ditlResID,
  923.                                     volatile    DlgHookYDProcPtr,
  924.                                     volatile    ModalFilterYDUPP,
  925.                                                 void *                    volatile    yourDataPtr        )
  926. {
  927.     #pragma unused(ditlResID)
  928.     #pragma unused(yourDataPtr)
  929.     OSErr err = noErr;
  930.  
  931.     //
  932.     //    PACK 3 ALWAYS clears the record.
  933.     //    PACK 3 also fills in some fields even
  934.     //    when sfGood is false. I'm not sure how to
  935.     //    translate this, so I'm just going to punt it.
  936.     //    Nav's model is just too different.
  937.     //
  938.  
  939.     MOASI_ClearStandardFileReply (*reply);
  940.  
  941.     if (NavServicesAvailable ( ))
  942.         err = MOASI_PutFile (prompt,defaultName,reply);
  943.  
  944.     return err;
  945. }
  946.  
  947. pascal OSErr MOASI_StandardGetFile (    volatile    FileFilterUPP                        fileFilter,
  948.                                         volatile    short                                numTypes,
  949.                                         volatile    ConstSFTypeListPtr                    typeList,
  950.                                                     StandardFileReply *        volatile    reply        )
  951. {
  952.     //
  953.     //    Call CustomGetFileCore with mostly "no-op" parameters,
  954.     //    transforming the simple file filter into a YD filter,
  955.     //    and specifying that we do not want to filter directories.
  956.     //
  957.  
  958.     OSErr err = noErr;
  959.  
  960.     FileFilterYDUPP ydupp = nil;
  961.  
  962.     if (fileFilter)
  963.     {
  964.         ydupp = NewFileFilterYDProc (MOASI_FileSystemFilterBridgeFromYD);
  965.  
  966.         if (!ydupp)
  967.         {
  968.             MOASI_ClearStandardFileReply (*reply);
  969.             err = MemError ( );
  970.         }
  971.     }
  972.  
  973.     if (!err)
  974.     {
  975.         err = MOASI_CustomGetFileCore (ydupp,numTypes,typeList,reply,0,nil,nil,fileFilter,false);
  976.         if (ydupp) DisposeRoutineDescriptor (ydupp);
  977.     }
  978.  
  979.     return err;
  980. }
  981.  
  982. pascal OSErr MOASI_StandardPutFile (    volatile    ConstStr255Param                    prompt,
  983.                                         volatile    ConstStr255Param                    defaultName,
  984.                                                     StandardFileReply *        volatile    reply            )
  985. {
  986.     //
  987.     //    Just call CustomPut with some "no-op" parameters.
  988.     //
  989.  
  990.     return MOASI_CustomPutFile (prompt,defaultName,reply,0,nil,nil,nil);
  991. }
  992.  
  993. pascal OSErr MOASI_StandardOpenDialog (StandardFileReply * volatile reply)
  994. {
  995.     //
  996.     //    Just call StandardGet with some sentinel values (see Translation Manager chapter).
  997.     //
  998.  
  999.     return MOASI_StandardGetFile (nil,kUseOpenResourceTypes,nil,reply);
  1000. }
  1001.  
  1002. pascal OSErr MOASI_StandardGetFilePreview (    volatile    FileFilterUPP                        fileFilter,
  1003.                                             volatile    short                                numTypes,
  1004.                                             volatile    ConstSFTypeListPtr                    typeList,
  1005.                                                         StandardFileReply *        volatile    reply        )
  1006. {
  1007.     //
  1008.     //    Nav assimilates the QuickTime file browsing feature set,
  1009.     //    so just call through.
  1010.     //
  1011.  
  1012.     return MOASI_StandardGetFile (fileFilter,numTypes,typeList,reply);
  1013. }
  1014.  
  1015. pascal OSErr MOASI_CustomGetFilePreview (    volatile    FileFilterYDUPP                        fileFilter,
  1016.                                             volatile    short                                numTypes,
  1017.                                             volatile    ConstSFTypeListPtr                    typeList,
  1018.                                                         StandardFileReply *        volatile    reply,
  1019.                                             volatile    short                                ditlResID,
  1020.                                             volatile    DlgHookYDProcPtr                    dialogHook,
  1021.                                             volatile    ModalFilterYDUPP                    modalFilter,
  1022.                                                         void *                    volatile    yourDataPtr        )
  1023. {
  1024.     //
  1025.     //    Nav assimilates the QuickTime file browsing feature set,
  1026.     //    so just call through.
  1027.     //
  1028.  
  1029.     return MOASI_CustomGetFile (fileFilter,numTypes,typeList,reply,ditlResID,dialogHook,modalFilter,yourDataPtr);
  1030. }
  1031.